package net.teamclerks.rpgf.inventory;

import java.util.ArrayList;
import java.util.List;

import net.teamclerks.rpgf.inventory.item.Item;
import net.teamclerks.rpgf.inventory.item.equipment.WeaponItem;
import net.teamclerks.rpgf.script.director.Director;

/**
 * Inventory essentially acts as the "ItemController" to parallel
 * with the model-view-controller feel to which RPGF, otherwise, 
 * strictly adheres.<br/>
 * <br/>
 * Inventory controls all aspects of Item interaction including
 * using items, acquiring items, losing items, etc.
 * @author msmith
 *
 */
public class Inventory
{
  /// DATAPOINTS ///
  protected List<Item> items;

  /// CONTROL DATA ///
  /**
   * Reference to the system's Director.
   */
  protected Director director;
  
  /// DENY CONSTRUCTOR ///
  private Inventory(){}
  
  /// PUBLIC CONSTRUCTOR ///
  public Inventory(Director director)
  {
    this();
    this.director = director;
    this.director.setInventory(this);
  }
  
  /// PUBLIC FINAL METHODS ///
  public final ArrayList<WeaponItem> getWeaponItems()
  {
    ArrayList<WeaponItem> weapons = new ArrayList<WeaponItem>();
    for(Item item: this.items)
    {
      if(item instanceof WeaponItem)
      {
        weapons.add((WeaponItem)item);
      }
    }
    return weapons;
  }
  /**
   * Adds a given Item to this Inventory if it does not already
   * exist in the Inventory. Otherwise, the count of the Item
   * is simply incremented in the Inventory.
   * @param item
   */
  public final void acquireItem(Item item)
  {
    if(this.items != null)
    {
      if(this.items.contains(item))
      {
        this.items.get(this.items.indexOf(item)).setCount(
            this.items.get(this.items.indexOf(item)).getCount()+1);
        if(this.items.get(this.items.indexOf(item)).getCount() == 1)
        {
          // This is the "first" acquisition of this item, it is now
          // possessed by the player.
          this.items.get(this.items.indexOf(item)).setPossessedByPlayer(true); 
        }
      }
      else
      {
        this.items.add(item);
        this.items.get(this.items.indexOf(item)).setCount(1);
        this.items.get(this.items.indexOf(item)).setPossessedByPlayer(true);
      }
    }
  }
  /**
   * Removes a specific Item from this Inventory by way of the count.
   * @param item
   */
  public final void loseItem(Item item)
  {
    if(this.items != null)
    {
      if(this.items.contains(item) && this.items.get(this.items.indexOf(item)).getCount() > 1)
      {
        this.items.get(this.items.indexOf(item)).setCount(
            this.items.get(this.items.indexOf(item)).getCount()-1);
      }
      else if(this.items.contains(item))
      {
        // LoseItem never loses Items... they have a count of zero and are
        // no longer possessed by the player.
        this.items.get(this.items.indexOf(item)).setCount(0);
        this.items.get(this.items.indexOf(item)).setPossessedByPlayer(false);
      }
    }
  }
  /**
   * Attempts to use a specific Item from this Inventory.<br/>
   * <br/>
   * This call will fail (return false) if any of the following are met:<br/>
   * &nbsp;&nbsp;&nbsp;1) The Item is not in this inventory.<br/>
   * &nbsp;&nbsp;&nbsp;2) The count for this Item in this Inventory is zero.<br/>
   * &nbsp;&nbsp;&nbsp;3) The Item returns false for the call to <code>isUsable()</code>.<br/>
   * &nbsp;&nbsp;&nbsp;4) The Item returns false for the call to <code>isPossessedByPlayer()</code>.
   * @param item
   * @return whether the item was "used"
   */
  public final boolean useItem(Item item)
  {    
    if(item != null && item.isUsable() && this.items.contains(item) && item.getCount() > 0 && item.isPossessedByPlayer())
    {
      // Attempt to use it!
      if(item.getItemAction() == null)
      {
        // This item has no action, thus it was not "used".
        return false;
      }
      // Tell the item's ItemAction to act.
      // At this point, we are setting precedence: if an Item's ItemAction is
      // called to perform its action, regardless of the result of said action as
      // doAction has no return value, then we will consider the Item to be "used".
      // Maybe the Item's ItemAction used a potion on a dead target, or attempted
      // to "use" an Item with no "use-case" - oh well, we will consider the action
      // successfully used, and act accordingly.
      //
      // IMPORTANT - all implementations of ItemAction need to implement their act
      // method appropriately for said item. That is, if you want a "Potion" Item 
      // and a "Hi-Potion" Item, you will need to implement an ItemAction for each
      // (or create anonymous ItemAction classes which override act accordingly).
      item.getItemAction().doAction();
      
      // The ItemAction has been fired, so we have done the action of healing a target
      // or returning mana to a target or returning a target to life etc all from the
      // ItemAction's implementation.
      // NOW, we need to determine whether that Item should decrement, disappear from
      // the Inventory, etc. More specific implementations will handle this directly
      // in the act method, but for simple ItemActions, we can do this here.
      if(item.isConsumable())
      {
        // Then we will attempt to consume it.
        item.setCount(item.getCount() - 1);
        if(item.getCount() == 0)
        {
          item.setPossessedByPlayer(false);
        }
      }
      
      return true;
    }
    
    return false;
  }
  /**
   * Gets whether the given Item is possessed by the player, exists
   * in the Inventory, and the count of the item is greater than zero.
   * @param item
   * @return possessed
   */
  public final boolean playerPossessesItem(Item item)
  {
    return this.items != null && this.items.contains(item) && 
       this.items.get(this.items.indexOf(item)).getCount() > 0 &&
       this.items.get(this.items.indexOf(item)).isPossessedByPlayer();
  }
  /**
   * Adds an item to the list of items if it does
   * not already exist.
   */
  public final void addItem(Item item)
  {
    if(!this.items.contains(item))
    {
      this.items.add(item);
    }
  }
  /**
   * Gets all the Items associated with this game.
   */
  public final List<Item> getItems()
  {
    return this.items;
  }
  /**
   * Sets all the Items associated with this game.
   * @param items
   */
  public final void setItems(List<Item> items)
  {
    this.items = items;
  }
  /**
   * Looks up a particular Item by its Id.
   * @param id
   * @return item
   */
  public final Item getItemById(long id)
  {
    for(Item item: items)
    {
      if(item.getId() == id)
      {
        return item;
      }
    }
    return null;
  }
}
